<template>
    <div class="json-schema-editor">
        <a-row v-if="deep !== 0" class="row" :gutter="10">
            <a-col :span="7" class="ant-col-name">
                <div :style="{ marginLeft: `${20 * deep}px` }" class="ant-col-name-c">
                    <a-button
                        v-if="pickValue.type === 'object' || pickValue.type === 'array'"
                        type="link"
                        :icon="hidden ? 'caret-right' : 'caret-down'"
                        style="color: rgba(0, 0, 0, 0.65);"
                        @click="hidden = !hidden"
                    />
                    <span v-else style="width: 32px; display: inline-block;" />
                    <a-input
                        :disabled="disabled || root || (pickValue.type === 'object' && isArrayItem)" 
                        :value="pickKey"
                        class="ant-col-name-input"
                        @blur="onInputName"
                    />
                </div>
                <a-tooltip v-if="root">
                    <!-- 全选 -->
                    <!-- <span slot="title" v-text="local['checked_all']"></span>
          <a-checkbox
            :disabled="!isObject && !isArray"
            class="ant-col-name-required"
            @change="onRootCheck"
          /> -->
                </a-tooltip>
                <a-tooltip v-else>
                    <!-- 是否必填 -->
                    <!-- <span slot="title" v-text="local['required']"></span>
          <a-checkbox
            :disabled="isItem"
            :checked="checked"
            class="ant-col-name-required"
            @change="onCheck"
          /> -->
                </a-tooltip>
            </a-col>
            <a-col :span="3">
                <a-select
                    v-model="pickValue.type"
                    class="ant-col-type"
                    :get-popup-container="
                        (triggerNode) => {
                            return triggerNode.parentNode || document.body;
                        }
                    "
                    @change="onChangeType"
                >
                    <a-select-option v-for="t in TYPE_NAME" :key="t">
                        {{ t }}
                    </a-select-option>
                </a-select>
            </a-col>
            <a-col :span="4">
                <a-input
                    :value="pickValue.title"
                    class="ant-col-title"
                    :placeholder="local['title']"
                    :disabled="isDefaultEmpty || pickValue.type === 'object'"
                    @blur="onInputTitle"
                />
            </a-col>

            <!-- 默认值 如果类型是整形或者浮点型，需要限制输入，只能输入数字，并且用函数实现-->
            <a-col :span="4">
                <template v-if="pickValue.type === 'bool'">
                    <a-select
                        :value="pickValue.default"
                        class="ant-col-title"
                        :placeholder="local['default']"
                        :disabled="hasKeyID"
                        style="width: 100%;"
                        @change="onInputDefault"
                    >
                        <a-select-option value="true">true</a-select-option>
                        <a-select-option value="false">false</a-select-option>
                    </a-select>
                </template>
                <template v-else>
                    <a-input
                        :value="pickValue.default"
                        class="ant-col-title"
                        :placeholder="getTypePlaceholder(pickValue.type)"
                        :disabled="hasKeyID || pickValue.type === 'object'"
                        :type="['int16', 'int32', 'int64', 'uint16', 'uint32', 'uint64', 'float', 'double', 'byte', 'bit'].includes(pickValue.type) ? 'number' : 'text'"
                        style="width: 100%;"
                        @input="onInputDefault"
                        @blur="onInputDefault"
                        @keypress="preventInvalidInput"
                    />
                </template>
            </a-col>

            <a-col :span="6" class="ant-col-setting">
                <a-tooltip>
                    <!-- 高级设置 -->
                    <!-- <span slot="title" v-text="local['adv_setting']"></span>
          <a-button
            type="link"
            icon="setting"
            class="setting-icon"
            @click="onSetting"
          /> -->
                </a-tooltip>
                <a-tooltip v-if="!isObject && !isArray">
                    <!-- 关联变量 -->
                    <span slot="title" v-text="local['associative_variable']" />
                    <a-button
                        type="link"
                        icon="paper-clip"
                        class="setting-icon"
                        @click="onAssociativeSingle"
                    />
                </a-tooltip>
                <a-tooltip v-if="isObject || isArray">
                    <!-- 批量关联 -->
                    <span slot="title" v-text="local['associative_batch']" />
                    <a-button
                        type="link"
                        icon="unordered-list"
                        class="setting-icon"
                        @click="onAssociativeBatch"
                    />
                </a-tooltip>
                <a-tooltip v-if="isObject || isArray">
                    <!-- 添加子节点 -->
                    <span slot="title" v-text="local['add_child_node']" />
                    <a-button
                        type="link"
                        icon="plus"
                        class="plus-icon"
                        @click="addChild"
                    />
                </a-tooltip>
                <a-tooltip v-if="!root && !isItem">
                    <!-- 删除节点 -->
                    <span slot="title" v-text="local['remove_node']" />
                    <a-button
                        type="link"
                        class="close-icon ant-btn-icon-only"
                        @click="removeNode"
                    >
                        <i aria-label="icon: plus" class="anticon anticon-plus">
                            <svg
                                viewBox="64 64 896 896"
                                data-icon="plus"
                                width="1em"
                                height="1em"
                                fill="currentColor"
                                aria-hidden="true"
                                focusable="false"
                                class=""
                            >
                                <path
                                    d="M810.666667 273.493333L750.506667 213.333333 512 451.84 273.493333 213.333333 213.333333 273.493333 451.84 512 213.333333 750.506667 273.493333 810.666667 512 572.16 750.506667 810.666667 810.666667 750.506667 572.16 512z"
                                    p-id="1142"
                                />
                            </svg>
                        </i>
                    </a-button>
                </a-tooltip>
            </a-col>
        </a-row>
        <template v-if="!hidden && pickValue.properties">
            <json-schema-editor
                v-for="(item, key, index) in pickValue.properties"
                :key="index"
                :value="{ [key]: item }"
                :parent="pickValue"
                :deep="deep + 1"
                :root="false"
                class="children"
                :lang="lang"
                :custom="custom"
            />
        </template>

        <a-modal
            v-if="modalVisible"
            v-model="modalVisible"
            :title="local['adv_setting']"
            :mask-closable="false"
            :ok-text="local['ok']"
            :cancel-text="local['cancel']"
            width="800px"
            dialog-class="json-schema-editor-advanced-modal"
            @ok="handleOk"
        >
            <!-- 基础设置 -->
            <h3 v-text="local['base_setting']" />
            <a-form v-model="advancedValue" class="ant-advanced-search-form">
                <a-row :gutter="6">
                    <a-col v-for="(item, key) in advancedValue" :key="key" :span="8">
                        <a-form-item>
                            <span>{{ local[key] }}</span>
                            <a-input-number
                                v-if="
                                    advancedAttr[key].type === 'integer' ||
                                        advancedAttr[key].type === 'number'
                                "
                                v-model="advancedValue[key]"
                                style="width: 100%;"
                                :placeholder="key"
                            />
                            <span
                                v-else-if="advancedAttr[key].type === 'boolean'"
                                style="display: inline-block; width: 100%;"
                            >
                                <a-switch v-model="advancedValue[key]" />
                            </span>
                            <a-textarea
                                v-else-if="key === 'enum'"
                                :value="enumText"
                                :rows="2"
                                :placeholder="local['enum_msg']"
                                @blur="changeEnumValue"
                            />
                            <a-select
                                v-else-if="advancedAttr[key].type === 'array'"
                                v-model="advancedValue[key]"
                                style="width: 100%;"
                                :get-popup-container="
                                    (triggerNode) => {
                                        return triggerNode.parentNode || document.body;
                                    }
                                "
                                :placeholder="local[key]"
                            >
                                <a-select-option value="">
                                    {{
                                        local["nothing"]
                                    }}
                                </a-select-option>
                                <a-select-option v-for="t in advancedAttr[key].enums" :key="t">
                                    {{ t }}
                                </a-select-option>
                            </a-select>
                            <a-input
                                v-else
                                v-model="advancedValue[key]"
                                style="width: 100%;"
                                :placeholder="key"
                            />
                        </a-form-item>
                    </a-col>
                </a-row>
            </a-form>
            <!-- 添加自定义属性 -->
            <h3 v-show="custom" v-text="local['add_custom']" />
            <a-form v-show="custom" class="ant-advanced-search-form">
                <a-row :gutter="6">
                    <a-col v-for="item in customProps" :key="item.key" :span="8">
                        <a-form-item :label="item.key">
                            <a-input v-model="item.value" style="width: calc(100% - 30px);" />
                            <a-button
                                icon="close"
                                type="link"
                                style="width: 30px;"
                                @click="removeCustomNode(item.key)"
                            />
                        </a-form-item>
                    </a-col>
                    <a-col v-show="addProp.key != undefined" :span="8">
                        <a-form-item>
                            <a-input
                                slot="label"
                                v-model="addProp.key"
                                style="width: 100px;"
                            />
                            <a-input v-model="addProp.value" style="width: 100%;" />
                        </a-form-item>
                    </a-col>
                    <a-col :span="8">
                        <a-form-item>
                            <a-button
                                v-if="customing"
                                icon="check"
                                type="link"
                                @click="confirmAddCustomNode(null)"
                            />
                            <a-tooltip v-else :title="local['add_custom']">
                                <a-button
                                    icon="plus"
                                    type="link"
                                    @click="addCustomNode"
                                />
                            </a-tooltip>
                        </a-form-item>
                    </a-col>
                </a-row>
            </a-form>
            <!-- 预览 -->
            <h3 v-text="local['preview']" />
            <pre style="width: 100%;">{{ completeNodeValue }}</pre>
        </a-modal>

        <AssociativeVarDialog
            :visible="associativeVarDialogVisible"
            :dialog-title="associativeVarDialogTitle"
            :table-row-select-type="associativeVarDialogRowSelectType"
            @handleCancel="associativeVarDialogVisible = false"
            @handleOk="handleAssociativeVarBatchDialogOk"
            @handleDevOk="handleAssociativeDevDialogOk"
        />

        <AssociativeVarDialog
            :visible="associativeVarBatchDialogVisible"
            :dialog-title="associativeVarBatchDialogTitle"
            @handleCancel="associativeVarBatchDialogVisible = false"
            @handleOk="handleAssociativeVarBatchDialogOk"
        />
    </div>
</template>
<script>
import Vue from 'vue'
import { isNull, renamePropertyAndKeepKeyPrecedence } from './util'
import { TYPE_NAME, TYPE } from './type/type'
import {
    Row,
    Col,
    Button,
    Input,
    InputNumber,
    Icon,
    Select,
    Tooltip,
    Modal,
    Form,
    Switch
} from 'ant-design-vue'
import LocalProvider from './LocalProvider'
import AssociativeVarDialog from './components/AssociativeVarDialog.vue'
import {
    GetGenerateUniqueId
} from './components/JsonManager'

Modal.install(Vue)
export default {
    name: 'JsonSchemaEditor',
    components: {
        ARow: Row,
        ACol: Col,
        AButton: Button,
        // eslint-disable-next-line vue/no-unused-components
        AIcon: Icon,
        AInput: Input,
        AInputNumber: InputNumber,
        ATextarea: Input.TextArea,
        ASelect: Select,
        ASelectOption: Select.Option,
        ATooltip: Tooltip,
        AModal: Modal,
        AForm: Form,
        AFormItem: Form.Item,
        ASwitch: Switch,
        AssociativeVarDialog
    },
    props: {
        value: {
            type: Object,
            required: true
        },
        disabled: {
            // name不可编辑，根节点name不可编辑,数组元素name不可编辑
            type: Boolean,
            default: false
        },
        disabledType: {
            // 禁用类型选择
            type: Boolean,
            default: false
        },
        isItem: {
            // 是否数组元素
            type: Boolean,
            default: false
        },
        deep: {
            // 节点深度，根节点deep=0
            type: Number,
            default: 0
        },
        root: {
            // 是否root节点
            type: Boolean,
            default: true
        },
        parent: {
            // 父节点
            type: Object,
            default: null
        },
        custom: {
            // enable custom properties
            type: Boolean,
            default: false
        },
        lang: {
            // i18n language
            type: String,
            default: 'zh_CN'
        }
    },
    data() {
        return {
            TYPE_NAME,
            hidden: false,
            countAdd: 1,
            modalVisible: false,
            advancedValue: {},
            addProp: {}, // 自定义属性
            customProps: [],
            customing: false,
            local: LocalProvider(this.lang),
            associativeVarDialogVisible: false,
            associativeVarBatchDialogVisible: false,
            associativeVarBatchDialogTitle: '批量关联',
            associativeVarDialogTitle: '关联变量',
            associativeVarDialogRowSelectType: 'radio'
        }
    },
    computed: { 
        pickValue() {
            return Object.values(this.value)[0]
        },
        pickKey() {
            console.log('pickKey', Object.keys(this.value)[0])
            return Object.keys(this.value)[0]
        },
        isObject() {
            return this.pickValue.type === 'object'
        },
        isArray() {
            return this.pickValue.type === 'array'
        },  
        isDefaultEmpty() {
            // if (this.pickValue.default === undefined || this.pickValue.default === '') {
            //     return false
            // }
            // return true
            return this.hasKeyID
        },  
        hasKeyID() {
            if (!this.pickValue.isDev && (this.pickValue.varId === undefined || this.pickValue.varId === '')) {
                return false
            }
            if (this.pickValue.isDev && (this.pickValue.devId === undefined || this.pickValue.devId === '')) {
                return false
            }            
            return true
        },        
        checked() {
            return (
                this.parent &&
                this.parent.required &&
                this.parent.required.indexOf(this.pickKey) >= 0
            )
        },
        advanced() {
            return TYPE[this.pickValue.type]
        },
        advancedAttr() {
            return TYPE[this.pickValue.type].attr
        },
        ownProps() {
            return [
                'type',
                'title',
                'default',
                'devId',
                'varId',
                'key',
                'isDev',
                'properties',
                'items',
                'required',
                ...Object.keys(this.advancedAttr)
            ]
        },
        advancedNotEmptyValue() {
            const jsonNode = Object.assign({}, this.advancedValue)
            for (let key in jsonNode) {
                isNull(jsonNode[key]) && delete jsonNode[key]
            }
            return jsonNode
        },
        completeNodeValue() {
            const t = {}
            const basicValue = { ...this.pickValue }
            for (const item of this.customProps) {
                t[item.key] = item.value
            }
            this._pickDiffKey().forEach(key => delete basicValue[key])
            return Object.assign({}, basicValue, t, this.advancedNotEmptyValue)
        },
        enumText() {
            const t = this.advancedValue['enum']
            if (!t) return ''
            if (!t.length) return ''
            return t.join('\n')
        },
        isArrayItem() {
            // 检查父节点是否存在且是数组类型
            return this.parent && this.parent.type === 'array'
        }
    },
    methods: {
        onInputName(e) {
            const oldKey = this.pickKey
            const newKey = e.target.value
            if (oldKey === newKey) return

            const _this = this
            if (null === this.parent) {
                renamePropertyAndKeepKeyPrecedence(_this, this.value, [oldKey, newKey])
                return
            } else {
                renamePropertyAndKeepKeyPrecedence(_this, this.parent.properties, [
                    oldKey,
                    newKey
                ])
            }

            // required重新设置
            const requireds = this.parent.required || []
            const oldIndex = requireds.indexOf(oldKey)
            if (requireds.length > 0 && oldIndex > -1) {
                requireds.splice(oldIndex, 1)
                requireds.push(newKey)
                this.$set(this.parent, 'required', [...new Set(requireds)])
            }
        },
        setInputName(newKey) {
            const oldKey = this.pickKey
            if (oldKey === newKey) return

            const _this = this
            if (null === this.parent) {
                renamePropertyAndKeepKeyPrecedence(_this, this.value, [oldKey, newKey])
                return
            } else {
                renamePropertyAndKeepKeyPrecedence(_this, this.parent.properties, [
                    oldKey,
                    newKey
                ])
            }

            // required重新设置
            const requireds = this.parent.required || []
            const oldIndex = requireds.indexOf(oldKey)
            if (requireds.length > 0 && oldIndex > -1) {
                requireds.splice(oldIndex, 1)
                requireds.push(newKey)
                this.$set(this.parent, 'required', [...new Set(requireds)])
            }
        },    
        onInputTitle(e) {
            this.$set(this.pickValue, 'title', e.target.value)
        },
        onInputDefault(e) {
            const value = e.target ? e.target.value : e
            const type = this.pickValue.type
            if (type === 'object' || type === 'array' || type === 'bool') {
                this.$set(this.pickValue, 'default', value)
                return
            }
            let convertedValue = value
            switch (type) {
                case 'bool':
                    convertedValue = value === 'true'
                    break
                case 'uint16':
                case 'uint32':
                case 'uint64':
                case 'int16':
                case 'int32':
                case 'int64':
                case 'byte':
                case 'bit':
                    convertedValue = parseInt(value)
                    break
                case 'float':
                case 'double':
                    convertedValue = parseFloat(value)
                    break
            }
            this.$set(this.pickValue, 'default', convertedValue)
        },    
        onChangeType() {
            this.parseCustomProps()
            // 删除自定义属性
            this.customProps.forEach(item => {
                this.$delete(this.pickValue, item.key)
            })
            this.customProps = []

            this.$delete(this.pickValue, 'properties')
            this.$delete(this.pickValue, 'items')
            this.$delete(this.pickValue, 'required')
            this.$delete(this.pickValue, 'format')
            this.$delete(this.pickValue, 'enum')

            // if (this.isArray) {
            //   this.$set(this.pickValue, "items", { type: "string" });
            // }
        },
        onCheck(e) {
            this._checked(e.target.checked, this.parent)
        },
        onRootCheck(e) {
            this._deepCheck(e.target.checked, this.pickValue)
        },
        changeEnumValue(e) {
            const pickType = this.pickValue.type
            const value = e.target.value

            if (!value || value === '') {
                this.advancedValue.enum = null
                return
            }
            var arr = value.split('\n')

            if (pickType === 'string') {
                this.advancedValue.enum = arr.map(item => item)
            } else {
                if (arr.length === 0 || (arr.length === 1 && arr[0] == '')) {
                    this.advancedValue.enum = null
                } else {
                    this.advancedValue.enum = arr.map(item => +item)
                }
            }
        },
        _deepCheck(checked, node) {
            if (node.type === 'object' && node.properties) {
                checked
                    ? this.$set(node, 'required', Object.keys(node.properties))
                    : this.$delete(node, 'required')
                Object.keys(node.properties).forEach(key =>
                    this._deepCheck(checked, node.properties[key])
                )
            } else if (node.type === 'array' && node.items.type === 'object') {
                checked
                    ? this.$set(
                        node.items,
                        'required',
                        Object.keys(node.items.properties)
                    )
                    : this.$delete(node.items, 'required')
                Object.keys(node.items.properties).forEach(key =>
                    this._deepCheck(checked, node.items.properties[key])
                )
            }
        },
        _checked(checked, parent) {
            let required = parent.required
            if (checked) {
                required || this.$set(this.parent, 'required', [])

                required = this.parent.required
                required.indexOf(this.pickKey) === -1 && required.push(this.pickKey)
            } else {
                const pos = required.indexOf(this.pickKey)
                pos >= 0 && required.splice(pos, 1)
            }
            required.length === 0 && this.$delete(parent, 'required')
        },
        addChild() {
            const name = this._joinName()
            const node = this.pickValue
            const temp = GetGenerateUniqueId()
      
            // 如果当前节点是数组类型，子节点必须是对象类型
            const type = this.isArray ? 'object' : 'string'
      
            node.properties || this.$set(node, 'properties', {})
            const props = node.properties
      
            // 如果是数组类型，添加一个带有 properties 的对象
            if (this.isArray) {
                this.$set(props, temp, { 
                    type: type, 
                    title: '', 
                    name: name,
                    properties: {} 
                })
            } else {
                this.$set(props, temp, { 
                    type: type, 
                    title: '', 
                    name: name 
                })
            }
        },
        parseCustomProps() {
            const ownProps = this.ownProps
            Object.keys(this.pickValue).forEach(key => {
                if (ownProps.indexOf(key) === -1) {
                    this.confirmAddCustomNode({ key: key, value: this.pickValue[key] })
                    // this.$delete(this.pickValue,key)
                }
            })
        },
        addCustomNode() {
            this.$set(this.addProp, 'key', this._joinName())
            this.$set(this.addProp, 'value', '')
            this.customing = true
        },
        removeCustomNode(key) {
            this.customProps.forEach((item, index) => {
                if (item.key === key) {
                    this.customProps.splice(index, 1)
                    return
                }
            })
        },
        confirmAddCustomNode(prop) {
            const p = prop || this.addProp
            let existKey = false
            this.customProps.forEach(item => {
                if (item.key === p.key) {
                    existKey = true
                }
            })
            if (existKey) return
            this.customProps.push(p)
            this.addProp = {}
            this.customing = false
        },
        removeNode() {
            const { properties, required } = this.parent
            this.$delete(properties, this.pickKey)
            if (required) {
                const pos = required.indexOf(this.pickKey)
                pos >= 0 && required.splice(pos, 1)
                required.length === 0 && this.$delete(this.parent, 'required')
            }
        },
        _joinName() {
            return `field_${this.deep}_${this.countAdd++}`
        },
        onSetting() {
            this.modalVisible = true
            this.advancedValue = { ...this.advanced.value }
            for (const k in this.advancedValue) {
                if (this.pickValue[k]) {
                    this.advancedValue[k] = this.pickValue[k]
                }
            }
            this.parseCustomProps()
        },
        handleOk() {
            this.modalVisible = false
            for (const key in this.advancedValue) {
                if (isNull(this.advancedValue[key])) {
                    this.$delete(this.pickValue, key)
                } else {
                    this.$set(this.pickValue, key, this.advancedValue[key])
                }
            }
            const diffKey = this._pickDiffKey()
            diffKey.forEach(key => this.$delete(this.pickValue, key))
            for (const item of this.customProps) {
                this.$set(this.pickValue, item.key, item.value)
            }
        },
        _pickDiffKey() {
            const keys = Object.keys(this.pickValue)
            return keys.filter(item => this.ownProps.indexOf(item) === -1)
        },
        onAssociativeSingle() {
            this.associativeVarDialogVisible = true
        },
        onAssociativeBatch() {
            this.associativeVarBatchDialogVisible = true
        },  
        handleAssociativeDevDialogOk(dataToJsonSchema) {
            this.associativeVarDialogVisible = false
            this.associativeVarBatchDialogVisible = false

            console.log('dataToJsonSchemaDev', dataToJsonSchema)
            const data = JSON.parse(dataToJsonSchema)
            // this.$set(this.pickValue, 'type', 'string')
            this.$set(this.pickValue, 'devId', data.devId)
            this.$set(this.pickValue, 'default', data.name)
            // this.$set(this.pickValue, 'varId', data.id)
            this.$set(this.pickValue, 'title', data.devName)
            this.$set(this.pickValue, 'isDev', true)
        },  
        handleAssociativeVarBatchDialogOk(dataToJsonSchema, type, isMonitor) {
            this.associativeVarDialogVisible = false
            this.associativeVarBatchDialogVisible = false

            console.log('dataToJsonSchema', dataToJsonSchema)

            const data = JSON.parse(dataToJsonSchema)
            // 监控点要特殊处理
            if (isMonitor && data.length > 0) {
                // data是一个数组，获取第一个即可
                const item = data[0]
                // this.setInputName(item.default)
                this.$set(this.pickValue, 'type', item.type)
                this.$set(this.pickValue, 'varId', item.varId)
                this.$set(this.pickValue, 'default', '')
                this.$set(this.pickValue, 'title', item.default)
                this.$set(this.pickValue, 'isDev', false)
                return 
            }

            // 如果是单选，是替换值，不是添加
            if (type === this.associativeVarDialogRowSelectType && data.length > 0) {
                const item = data[0]
                // this.setInputName(item.title)
                // this.$set(this.pickValue, 'type', item.type)
                this.$set(this.pickValue, 'devId', item.devId)
                this.$set(this.pickValue, 'key', item.key)
                this.$set(this.pickValue, 'varId', item.varId)
                this.$set(this.pickValue, 'title', item.title)
                this.$set(this.pickValue, 'isDev', false)
                return
            }     
        },     
        getTypePlaceholder(type) {
            switch (type) {
                case 'uint16': return '请输入0-65535的整数'
                case 'uint32': return '请输入0-4294967295的整数'
                case 'uint64': return '请输入非负整数'
                case 'int16': return '请输入-32768到32767的整数'
                case 'int32': return '请输入-2147483648到2147483647的整数'
                case 'int64': return '请输入整数'
                case 'float':
                case 'double': return '请输入数字'
                case 'byte':
                case 'bit': return '请输入0-255的整数'
                default: return this.local['default']
            }
        },
        getTypeMaxValue(type) {
            switch (type) {
                case 'uint16': return 65535
                case 'uint32': return 4294967295
                case 'uint64': return Number.MAX_SAFE_INTEGER
                case 'int16': return 32767
                case 'int32': return 2147483647
                case 'int64': return Number.MAX_SAFE_INTEGER
                case 'byte':
                case 'bit': return 255
                case 'float':
                case 'double': return Number.MAX_SAFE_INTEGER
                default: return Number.MAX_SAFE_INTEGER
            }
        },
        getTypeMinValue(type) {
            switch (type) {
                case 'uint16':
                case 'uint32':
                case 'uint64':
                case 'byte':
                case 'bit': return 0
                case 'int16': return -32768
                case 'int32': return -2147483648
                case 'int64': return Number.MIN_SAFE_INTEGER
                case 'float':
                case 'double': return Number.MIN_SAFE_INTEGER
                default: return Number.MIN_SAFE_INTEGER
            }
        },
        preventInvalidInput(e) {
            const type = this.pickValue.type
            const char = String.fromCharCode(e.keyCode || e.charCode)
            if (e.keyCode === 8 || e.keyCode === 46 || (e.keyCode >= 37 && e.keyCode <= 40)) return true
            const currentValue = this.pickValue.default || ''
            const newValue = currentValue + char
            switch (type) {
                case 'uint16':
                case 'uint32':
                case 'uint64':
                case 'byte':
                case 'bit':
                    if (!/^\d$/.test(char)) {
                        e.preventDefault()
                        this.$message.error(`请输入0-${this.getTypeMaxValue(type)}之间的整数`)
                        return false
                    }
                    if (parseInt(newValue) > this.getTypeMaxValue(type)) {
                        e.preventDefault()
                        this.$message.error(`请输入0-${this.getTypeMaxValue(type)}之间的整数`)
                        return false
                    }
                    break
                case 'int16':
                case 'int32':
                case 'int64':
                    if (!/^[-\d]$/.test(char)) {
                        e.preventDefault()
                        this.$message.error(`请输入${this.getTypeMinValue(type)}到${this.getTypeMaxValue(type)}之间的整数`)
                        return false
                    }
                    if (char === '-' && currentValue) {
                        e.preventDefault()
                        this.$message.error('负号只能在开头输入')
                        return false
                    }
                    if (char !== '-' && parseInt(newValue) > this.getTypeMaxValue(type)) {
                        e.preventDefault()
                        this.$message.error(`请输入${this.getTypeMinValue(type)}到${this.getTypeMaxValue(type)}之间的整数`)
                        return false
                    }
                    if (char !== '-' && parseInt(newValue) < this.getTypeMinValue(type)) {
                        e.preventDefault()
                        this.$message.error(`请输入${this.getTypeMinValue(type)}到${this.getTypeMaxValue(type)}之间的整数`)
                        return false
                    }
                    break
                case 'float':
                case 'double':
                    if (!/^[-\d.]$/.test(char)) {
                        e.preventDefault()
                        this.$message.error('请输入有效的数字')
                        return false
                    }
                    if (char === '-' && currentValue) {
                        e.preventDefault()
                        this.$message.error('负号只能在开头输入')
                        return false
                    }
                    if (char === '.' && currentValue.includes('.')) {
                        e.preventDefault()
                        this.$message.error('小数点只能输入一次')
                        return false
                    }
                    if (char !== '-' && char !== '.' && parseFloat(newValue) > this.getTypeMaxValue(type)) {
                        e.preventDefault()
                        this.$message.error('输入值超出范围')
                        return false
                    }
                    if (char !== '-' && char !== '.' && parseFloat(newValue) < this.getTypeMinValue(type)) {
                        e.preventDefault()
                        this.$message.error('输入值超出范围')
                        return false
                    }
                    break
            }
            return true
        }
    }  
}
</script>
<style scoped>
.json-schema-editor .row {
    display: flex;
    margin: 12px;
}
.json-schema-editor .row .ant-col-name {
    display: flex;
    align-items: center;
}
.json-schema-editor .row .ant-col-name .ant-col-name-c {
    display: flex;
    align-items: center;
}
.json-schema-editor .row .ant-col-name .ant-col-name-required {
    flex: 0 0 24px;
    text-align: center;
}
.json-schema-editor .row .ant-col-type {
    width: 100%;
}
.json-schema-editor .row .ant-col-setting {
    display: inline-block;
}
.json-schema-editor .row .setting-icon {
    color: rgba(0, 0, 0, 0.45);
    border: none;
}
.json-schema-editor .row .plus-icon {
    border: none;
}
.json-schema-editor .row .close-icon {
    color: #888;
    border: none;
}
</style>
<style>
.json-schema-editor-advanced-modal {
    color: rgba(0, 0, 0, 0.65);
    min-width: 600px;
}
.json-schema-editor-advanced-modal pre {
    font-family: monospace;
    height: 100%;
    overflow-y: auto;
    border: 1px solid rgba(0, 0, 0, 0.1);
    border-radius: 4px;
    padding: 12px;
    width: 50%;
}
.json-schema-editor-advanced-modal h3 {
    display: block;
    border-left: 3px solid #1890ff;
    padding: 0 8px;
}
.json-schema-editor-advanced-modal .ant-advanced-search-form .ant-form-item {
    display: flex;
}
.json-schema-editor-advanced-modal .ant-advanced-search-form .ant-form-item .ant-form-item-control-wrapper {
    flex: 1;
}
</style>